namespace LocalBoard.ServerControls
{
	using System;
	using System.Collections;
	using System.IO;
	using System.Text;
	using System.Web;
	using System.Web.UI;
	using System.Web.UI.WebControls;
	using System.ComponentModel;

	using LocalBoard.Domain;

	public enum MessageListViewType
	{
		BoardMessageListView,
		ClubMessageListView,
		BoardMessageListSmallView
	}

	/// <summary>
	/// Summary description for MessageTreeList.
	/// </summary>
	[DefaultProperty("Text"),
	ToolboxData("<{0}:MessageTreeList runat=server></{0}:MessageTreeList>")]
	[ParseChildren(true)]
	public class MessageTreeList : System.Web.UI.Control
	{
		public MessageTreeList()
		{
		}

		#region Fields for External Properties
		private string text;

		private int _currentMessageId;
		private ArrayList _messageList;
		private MessageListViewType _messageListType = MessageListViewType.BoardMessageListView;
		private bool _expandAll;
		#endregion

		#region Fields for External Templates Properties
		//Header
		private ITemplate _headerTemplate;
		//ClubMessageListHeaderTemplate Header
		private ITemplate _clubMessageListHeaderTemplate;
		//Message Item
		private ITemplate _messageItemTemplate;
		//ClubMessageListItemTemplate Message Item
		private ITemplate _clubMessageListItemTemplate;
		//Message Alternative Item
		private ITemplate _messageAltItemTemplate;
		//Footer
		private ITemplate _footerTemplate;
		//No Data Message
		private ITemplate _noDataMessageTemplate;
		//Hidden Style
		private ITemplate _hiddenStyleTemplate;
		//Expand Sign
		private ITemplate _expandSignTemplate;
		//Leaf Sign
		private ITemplate _leafSignTemplate;
		//Indentation
		private ITemplate _indentationTemplate;
		#endregion

		#region Properties for external
		[Bindable(true),
		Category("Appearance"),
		DefaultValue("")]
		public string Text
		{
			get	{	return text;	}
			set	{	text = value;	}
		}

		[Bindable(true),
		Category("Appearance"),
		DefaultValue(false)]
		public bool ExpandAll
		{
			get	{	return _expandAll;	}
			set	{	_expandAll = value;	}
		}

		[Bindable(true),
		Category("Appearance"),
		DefaultValue("")]
		public string MessageListType
		{
			get {return _messageListType.ToString();}
			set {_messageListType = value == null || value.Length == 0 ? MessageListViewType.BoardMessageListView : (MessageListViewType)Enum.Parse(typeof(MessageListViewType), value, true);}
		}

		[Bindable(true),
		Category("Data"),
		DefaultValue(0)]
		public int CurrentMessageId
		{
			get	{	return _currentMessageId;	}
			set	{	_currentMessageId = value;	}
		}

		[Bindable(true),
		Category("Data"),
		DefaultValue(null)]
		public ArrayList MessageList
		{
			get	{	return _messageList;	}
			set	{	_messageList = value;	}
		}
		#endregion

		#region Template Properties
		public ITemplate HeaderTemplate
		{
			get	{	return _headerTemplate;	}
			set	{	_headerTemplate = value;	}
		}

		public ITemplate ClubMessageListHeaderTemplate
		{
			get	{	return _clubMessageListHeaderTemplate;	}
			set	{	_clubMessageListHeaderTemplate = value;	}
		}

		[TemplateContainer(typeof(MessageItem))]
		public ITemplate MessageItemTemplate
		{
			get	{	return _messageItemTemplate;	}
			set	{	_messageItemTemplate = value;	}
		}

		[TemplateContainer(typeof(MessageItem))]
		public ITemplate ClubMessageListItemTemplate
		{
			get	{	return _clubMessageListItemTemplate;	}
			set	{	_clubMessageListItemTemplate = value;	}
		}

		[TemplateContainer(typeof(MessageItem))]
		public ITemplate MessageAltItemTemplate
		{
			get	{	return _messageAltItemTemplate;	}
			set	{	_messageAltItemTemplate = value;	}
		}

		public ITemplate FooterTemplate
		{
			get	{	return _footerTemplate;	}
			set	{	_footerTemplate = value;	}
		}

		public ITemplate NoDataMessageTemplate
		{
			get	{	return _noDataMessageTemplate;	}
			set	{	_noDataMessageTemplate = value;	}
		}

		public ITemplate HiddenStyleTemplate
		{
			get	{	return _hiddenStyleTemplate;	}
			set	{	_hiddenStyleTemplate = value;	}
		}

		[TemplateContainer(typeof(MessageItem))]
		public ITemplate ExpandSignTemplate
		{
			get	{	return _expandSignTemplate;	}
			set	{	_expandSignTemplate = value;	}
		}

		[TemplateContainer(typeof(MessageItem))]
		public ITemplate LeafSignTemplate
		{
			get	{	return _leafSignTemplate;	}
			set	{	_leafSignTemplate = value;	}
		}
		public ITemplate IndentationTemplate
		{
			get	{	return _indentationTemplate;	}
			set	{	_indentationTemplate = value;	}
		}
		#endregion

		#region Fields for internal
		private int _treeDepth = 0;
		private string _expandSignString = "";
		private string _leafSignString = "";
		private string _hiddenStyleString = "";
		private string _indentationString = "";
		private StringBuilder _messageListJavascript = new StringBuilder();
		private StringBuilder _setParentJavascript = new StringBuilder();

		private bool _throwExceptionIfTemplateIsNull = true;
		private bool _dontThrowExceptionIfTemplateIsNull = false;
		#endregion

		/// <summary>
		/// override the control creation and instantiate the template
		/// </summary>
		protected override void CreateChildControls()
		{
			GetTemplateContentAsString();

			Controls.Clear();

			ProcessHeaderTemplate();

			if(ShouldDisplayNoDataMessage())
					InstantiateTemplateInItemControlAndAdd(new CustomPanel(), _noDataMessageTemplate);
			else
				ProcessItemTemplate();

			ProcessFooterTemplate();

			if(!ShouldDisplayNoDataMessage())
				AddJavascriptLiteralControl();
		}

		private bool ShouldDisplayNoDataMessage()
		{
			return (_messageList == null || _messageList.Count == 0)
				&& _noDataMessageTemplate != null;
		}

		#region Helper Methods
		private void GetTemplateContentAsString()
		{
			_expandSignString = GetOutputFromInstantiatedTemplateInItemControl(new CustomPanel(), _expandSignTemplate);
			_leafSignString = GetOutputFromInstantiatedTemplateInItemControl(new CustomPanel(), _leafSignTemplate);
			_hiddenStyleString = GetOutputFromInstantiatedTemplateInItemControl(new CustomPanel(), _hiddenStyleTemplate);
			_indentationString = GetOutputFromInstantiatedTemplateInItemControl(new CustomPanel(), _indentationTemplate);
		}

		private void ProcessHeaderTemplate()
		{
			ITemplate headerTemplate = GetHeaderTemplate();
			if(headerTemplate != null)
				InstantiateTemplateInItemControlAndAdd(new CustomPanel(), headerTemplate);
		}

		private void ProcessItemTemplate()
		{
			_messageListJavascript.Append("<script language=\"javascript\">\r\n");
	
			InstantiateMessageTemplateList(_messageList);
	
			_messageListJavascript.Append(_setParentJavascript.ToString());

			if(_expandAll)
			{
				foreach(Message message in _messageList)
				{
					_messageListJavascript.Append("ShowChildren(message" + message.Id.ToString() + ");\r\n");
				}
			}
			
			if(_currentMessageId != 0)
				_messageListJavascript.Append("ExpandParents(message" + _currentMessageId.ToString() + ");\r\n");

			_messageListJavascript.Append("</script>\r\n");
		}

		private void ProcessFooterTemplate()
		{
			if(_footerTemplate != null)
				InstantiateTemplateInItemControlAndAdd(new CustomPanel(), _footerTemplate);
		}

		private void AddJavascriptLiteralControl()
		{
			Literal javascript = new Literal();
			javascript.Text = _messageListJavascript.ToString();
			Controls.Add(javascript);
		}

		private void InstantiateMessageTemplateList(ArrayList messageList)
		{
			int index = 0;
			foreach(Message message in messageList)
			{
				index++;
				ProcessMessageItself(index, message);

				if(MessageHasChildren(message))
				{
					ProcessChildren(message);
					GenerateJavascriptWithChildren(message);
				}
				else
					GenerateJavascriptMessageOnly(message);

				if(MessageHasParent(message))
					GenerateJavascriptForParent(message);
			}
		}

		private void ProcessMessageItself(int index, Message message)
		{
			ITemplate messageItemTemplate = ShouldUseAltItemTemplate(index) ? GetMessageAltItemTemplate() : GetMessageItemTemplate();
	
			if(messageItemTemplate != null)
				InstantiateTemplateInItemControlAndAdd(CreateItemControlFromMessage(message, _treeDepth), messageItemTemplate);
		}

		private void GenerateJavascriptForParent(Message message)
		{
			_setParentJavascript.Append(string.Format("SetMessageParent(message{0}, message{1});\r\n", message.Id, message.ReplyToId));
		}

		private static bool MessageHasParent(Message message)
		{
			return message.ReplyToId != 0;
		}

		private void GenerateJavascriptMessageOnly(Message message)
		{
			_messageListJavascript.Append(string.Format("var message{0} = new Message('message{0}', null);\r\n", message.Id));
		}

		private void GenerateJavascriptWithChildren(Message message)
		{
			StringBuilder childrenIdList = new StringBuilder();
			for(int i=0; i<message.Replies.Count; i++)
			{
				Message child = message.Replies[i] as Message;
				if(i > 0)
					childrenIdList.Append(",");
				childrenIdList.Append("message" + child.Id.ToString());
			}
			_messageListJavascript.Append(string.Format("var message{0} = new Message('message{0}', new Array({1}));\r\n", message.Id, childrenIdList));
		}

		private void ProcessChildren(Message message)
		{
			_treeDepth++;
			InstantiateMessageTemplateList(message.Replies);
			_treeDepth--;
		}

		private static bool MessageHasChildren(Message message)
		{
			return message.Replies != null && message.Replies.Count > 0;
		}

		private bool ShouldUseAltItemTemplate(int index)
		{
			return (index%2) == 1 && GetMessageAltItemTemplate() != null;
		}

		private ITemplate GetMessageAltItemTemplate()
		{
			return _messageAltItemTemplate;
		}

		private ITemplate GetMessageItemTemplate()
		{
			switch(_messageListType)
			{
//				case MessageListViewType.BoardMessageListView:
//					return _messageItemTemplate;
				case MessageListViewType.ClubMessageListView:
					return _clubMessageListItemTemplate;
				default:
					return _messageItemTemplate;
			}
		}

		private ITemplate GetHeaderTemplate()
		{
			switch(_messageListType)
			{
//				case MessageListViewType.BoardMessageListView:
//					return _messageItemTemplate;
				case MessageListViewType.ClubMessageListView:
					return _clubMessageListHeaderTemplate;
				default:
					return _headerTemplate;
			}
		}

		private void InstantiateTemplateInItemControlAndAdd(Control item, ITemplate template)
		{
			if(null != template)
			{
				template.InstantiateIn(item);
				item.DataBind();
				Controls.Add(item);
			}
		}

		private string GetOutputFromInstantiatedTemplateInItemControl(Control item, ITemplate template)
		{
			if(null != template)
			{
				template.InstantiateIn(item);
				item.DataBind();
				StringBuilder sb = new StringBuilder();
				StringWriter sw = new StringWriter(sb);
				HtmlTextWriter htmlTW = new HtmlTextWriter(sw);
				item.RenderControl(htmlTW);

				return sb.ToString();
				//return HttpContext.Current.Server.HtmlEncode(sb.ToString());
			}
			return "";
		}

		private MessageItem CreateItemControlFromMessage(Message message, int treeDepth)
		{
			string expandSign = "";
			MessageItem item = new MessageItem();

			if(message.ReplyToId > 0)
				item.Style = _hiddenStyleString;
			item.Id = message.Id.ToString();
			if(message.Replies != null && message.Replies.Count > 0)
				expandSign = string.Format(_expandSignString, message.Id);
			else
				expandSign = string.Format(_leafSignString, message.Id);
			item.ExpandSign = expandSign;
			
			StringBuilder indentationString = new StringBuilder();
			for(int index = 0; index<treeDepth; index++)
			{
				indentationString.Append(_indentationString);
			}
			item.MessageNo = message.No;
			item.Indentation = indentationString.ToString();
			item.Subject = message.Subject;
			item.PostedByUserName = message.PostedByUserName;
			item.PostedByUserId = message.PostedByUserId;
			item.Date = message.Date;
			item.Hits = message.Hits;
			item.BoardId = message.BoardId;
			item.ClubId = message.ClubId;
			item.Comments = message.Comments;
			item.IsChild = message.ReplyToId > 0;
			return item;
		}
		#endregion
	}
}
